package com.thiswhyme.common.web.exception;

import com.thiswhyme.common.conststant.CommonConstant;
import com.thiswhyme.common.expection.BusinessException;
import com.thiswhyme.common.response.ResponseCode;
import com.thiswhyme.common.response.ResponseVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.sql.SQLSyntaxErrorException;

/**
 * 统一异常处理
 * Created by thiswhyme on 2019-12-11.
 */
@Slf4j
public class BaseExceptionHandler {

    /**
     * 当前环境
     */
    @Value("${spring.profiles.active:pro}")
    private String profile;

    /**
     * 业务异常
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler({BusinessException.class})
    public ResponseVo<String> handlerBusinessException(HttpServletRequest request, BusinessException e) {
        log.error("业务异常，url: {}, errMsg: {}", request.getRequestURI(), e.getMessage(), e);
        return ResponseVo.failure(e.getCode(), e.getMsg());
    }

    /**
     * servlet 异常
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler({
            NoHandlerFoundException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
            HttpMediaTypeNotAcceptableException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            MissingServletRequestPartException.class,
            AsyncRequestTimeoutException.class
    })
    public ResponseVo<String> handleServletException(HttpServletRequest request, Exception e) {
        log.error("servlet异常，url: {}, errMsg: {}", request.getRequestURI(), e.getMessage(), e);
        if (isPro()) {
            return ResponseVo.failure(ResponseCode.SERVLET_EXCEPTION.getCode(), ResponseCode.SYSTEM_EXCEPTION.getMsg());
        }
        return ResponseVo.failure(ResponseCode.SERVLET_EXCEPTION.getCode(), e.getMessage());
    }


    /**
     * 参数校验异常
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    public ResponseVo<String> handleParamException(HttpServletRequest request, Exception e) {
        log.error("请求参数校验异常，url: {}, errMsg: {}", request.getRequestURI(), e.getMessage(), e);

        BindingResult bindingResult;
        if(e instanceof MethodArgumentNotValidException) {
            bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
        } else if (e instanceof BindException) {
            bindingResult = ((BindException) e).getBindingResult();
        } else {
            return ResponseVo.failure(ResponseCode.INVALID_ARGUMENT.getCode(), ResponseCode.INVALID_ARGUMENT.getMsg());
        }

        String msg = CollectionUtils.isEmpty(bindingResult.getAllErrors()) ? ResponseCode.INVALID_ARGUMENT.getMsg()
                : bindingResult.getAllErrors().get(0).getDefaultMessage();
        return ResponseVo.failure(ResponseCode.INVALID_ARGUMENT.getCode(), msg);
    }

    /**
     * 处理sql异常
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(SQLSyntaxErrorException.class)
    public ResponseVo<String> handleSqlException(HttpServletRequest request, SQLSyntaxErrorException e) {
        log.error("SQL异常，url: {}, errMsg: {}", request.getRequestURI(), e.getMessage(), e);
        if (isPro()) {
            return ResponseVo.failure(ResponseCode.SQL_EXCEPTION.getCode(), ResponseCode.SYSTEM_EXCEPTION.getMsg());
        }
        return ResponseVo.failure(ResponseCode.SQL_EXCEPTION.getCode(), e.getMessage());
    }

    /**
     * 未处理的服务器异常
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(Throwable.class)
    public ResponseVo<String> handleException(HttpServletRequest request, Throwable e) {
        log.error("未知异常，url: {}, errMsg: {}", request.getRequestURI(),
                e.getMessage(), e);
        if (isPro()) {
            return ResponseVo.failure(ResponseCode.SYSTEM_EXCEPTION.getCode(), ResponseCode.SYSTEM_EXCEPTION.getMsg());
        }
        return ResponseVo.failure(ResponseCode.SYSTEM_EXCEPTION.getCode(), e.getMessage());

    }

    /**
     * 是否预发布或线上环境
     * @return
     */
    private boolean isPro() {
        return CommonConstant.ENV_PRO.equals(profile) || CommonConstant.ENV_PRE.equals(profile);
    }
}
